let fact = function (n) {
    if (n < 2) {
        return 1;
    } else {
        return n * fact(n - 1);
    }
};

// 如果不允许将 fact 进行赋值，怎样在函数里面引用

// 第一次尝试，可以新建一个函数 将需要引用的 fact 方法传进去
// 但是这个是有问题的 因为对于 givenFact 是有依赖的
// 但 如果已经有一个 fact 函数的话 为什么还要搞这个 makeFact 呢 ..
let makeFact = function (givenFact) {
    return function (n) {
        if (n < 2) {
            return 1
        } else {
            return n * givenFact(n - 1)
        }
    };
}

// It turns out that it is possible though,
// because the fact function which makeFact makes doesn't always call givenFact.
// Instead of passing in a pre-made givenFact we can make givenFact itself use makeFact,
// until makeFact makes a fact call which doesn't need to call givenFact.
let makeRealFact = function (makeFact) {
    let tryFact = function (n) {
        let nextTryFact = makeFact(tryFact)
        return nextTryFact(n)
    }
    return makeFact(tryFact)
}

// console.dir(makeRealFact(makeFact)(5));

let foo = function (x) {
    if (x < 2) {
        return 1;
    } else {
        return x * arguments.callee(x - 1)
    }
}
let bar = foo;
foo = 'not a func';
bar(4);

/*
 We create a new function makeRealFact (our Y function) which uses makeFact to make the actual factorial function.
 The tryFact function is passed to makeFact to be used as its givenFact function.
 If makeFact needs to use givenFact it will call tryFact again, which will make another tryFact using makeFact and try again.
 Eventually makeFact will be able to return a factorial function which doesn't use givenFact,
 which can then be used to find a givenFact, and that used to find another givenFact, and so on.
*/

// 不动点是一个值(当然了函数也可以是一个值)
// f(x) = x，x 则被称之为 fixed pointed
// f(f1) = f1，f1 是 f 的一个不动点，这里 f1 本身就是一个函数
// f(Y(f)) = Y(f) ，意思就是，用 f 调用 Y 会得到一个 f 自己的的不动点

((f, x) => {
    if (x < 2) {
        return 1
    } else {
        return x * f(f, x - 1)
    }
})(f, 5);

(f => x => {
    if (x < 2) {
        return 1
    } else {
        return x * f(f)(x - 1)
    }
})(f)(5);

(f => x => {
    let h = (q, x) => {
        if (x < 2) {
            return 1
        } else {
            return x * q(x - 1)
        }
    }
    // 这里有一个 f(f)
    return h(f(f), x)
})(f)(5)

let f = function (q) {
    return function (x) {
        return x === 0 ? 1 : x * q(x - 1);
    };
}

// 这个就是 Y 的原型
let factorial = function (h) {
    return function (x) {
        return f(h(h))(x);
    };
}

let Y = function (f) {
    let g = function (h) {
        return function (x) {
            return f(h(h))(x);
        };
    };
    return g(g);
}

let Y = function (f) {
    return (function (g) {
        return g(g);
    })(function (h) {
        return function () {
            return f(h(h)).apply(null, arguments);
        };
    });
}

let factorial = Y(
    function (recurse) {
        return function (x) {
            return x === 0 ? 1 : x * recurse(x - 1);
        };
    }
)

let Y = function (f) {
    let g = function (h) {
        return function (x) {
            return (
                function (recurse) {
                    return function (x) {
                        return x == 0 ? 1 : x * recurse(x - 1)
                    }
                }
            )(h(h))(x)
        };
    };
    return g(g)
}

factorial(5)
